package cool.doudou.doudada.pay.rest.service;

import com.alibaba.fastjson2.JSONObject;
import cool.doudou.doudada.cipher.algorithm.util.AesUtil;
import cool.doudou.doudada.pay.core.enums.PayMode;
import cool.doudou.doudada.pay.core.factory.ConsumerMapFactory;
import cool.doudou.doudada.pay.core.memory.WxPayMem;
import cool.doudou.doudada.pay.core.method.NotifyMethod;
import cool.doudou.doudada.pay.core.signer.WxSigner;
import cool.doudou.doudada.pay.core.signer.ZfbSigner;
import cool.doudou.doudada.pay.core.util.RespUtil;
import cool.doudou.doudada.pay.properties.WxPayProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.security.cert.X509Certificate;
import java.util.Base64;
import java.util.Map;

/**
 * PayNotifyService
 *
 * @author jiangcs
 * @since 2022/07/04
 */
@Slf4j
public class PayNotifyService {
    private WxPayProperties wxPayProperties;

    /**
     * 微信支付通知
     *
     * @param jsonStr  通知参数
     * @param request  请求句柄
     * @param response 响应句柄
     */
    public void wx(String jsonStr, HttpServletRequest request, HttpServletResponse response) {
        try {
            String timestamp = request.getHeader("Wechatpay-Timestamp");
            String nonceStr = request.getHeader("Wechatpay-Nonce");
            String signatureStr = request.getHeader("Wechatpay-Signature");
            String serial = request.getHeader("Wechatpay-Serial");
            X509Certificate x509Certificate = WxPayMem.certificateMap.get(serial);
            boolean verifyFlag = WxSigner.verify(x509Certificate, timestamp, nonceStr, jsonStr, signatureStr);
            if (!verifyFlag) {
                throw new RuntimeException("签名验证失败");
            }

            JSONObject bodyObj = JSONObject.parseObject(jsonStr, JSONObject.class);
            JSONObject resourceObj = bodyObj.getJSONObject("resource");
            String nonce = resourceObj.getString("nonce");
            String ciphertext = resourceObj.getString("ciphertext");
            String associatedData = resourceObj.getString("associated_data");
            String decryptStr = AesUtil.decryptGCM(wxPayProperties.getApiKeyV3(), nonce, ciphertext, associatedData);

            NotifyMethod notifyMethod = ConsumerMapFactory.get(PayMode.WX);
            if (notifyMethod != null) {
                Object bean = notifyMethod.getBean();
                Method method = notifyMethod.getMethod();
                try {
                    method.setAccessible(true);
                    Boolean respFlag = (Boolean) method.invoke(bean, decryptStr);
                    if (respFlag) {
                        RespUtil.writeSuccess(response, "");
                        return;
                    }
                } catch (Exception e) {
                    log.error("bean[{}].method[{}] invoke exception: ", bean, method.getName(), e);
                }
                throw new RuntimeException("业务方法处理异常");
            } else {
                log.warn("No NotifyMethod found");

                RespUtil.writeSuccess(response, "");
            }
        } catch (Exception e) {
            log.error("wxPay exception: ", e);

            JSONObject resultObj = new JSONObject();
            resultObj.put("code", "FAIL");
            resultObj.put("message", e.getMessage());
            RespUtil.writeFail(response, resultObj.toJSONString());
        }
    }

    /**
     * 支付宝支付通知
     *
     * @param request  请求句柄
     * @param response 响应句柄
     */
    public void zfb(HttpServletRequest request, HttpServletResponse response) {
        try {
            Map<String, String[]> paramMap = request.getParameterMap();
            // 除去 sign、sign_type 两个参数
            String signatureStr = new String(Base64.getDecoder().decode(paramMap.remove("sign")[0]));
            paramMap.remove("sign_type");

            boolean verifyFlag = ZfbSigner.verify(paramMap, signatureStr);
            if (!verifyFlag) {
                throw new RuntimeException("签名验证失败");
            }

            JSONObject jsonObject = new JSONObject();
            paramMap.keySet().forEach(key -> jsonObject.put(key, paramMap.get(key)[0]));

            NotifyMethod notifyMethod = ConsumerMapFactory.get(PayMode.ZFB);
            if (notifyMethod != null) {
                Object bean = notifyMethod.getBean();
                Method method = notifyMethod.getMethod();
                try {
                    method.setAccessible(true);
                    Boolean respFlag = (Boolean) method.invoke(bean, jsonObject.toString());
                    if (respFlag) {
                        RespUtil.writeSuccess(response, "success");
                        return;
                    }
                } catch (Exception e) {
                    log.error("bean[{}].method[{}] invoke exception: ", bean, method.getName(), e);
                }
                throw new RuntimeException("业务方法处理异常");
            } else {
                log.warn("No NotifyMethod found");

                RespUtil.writeSuccess(response, "success");
            }
        } catch (Exception e) {
            log.error("zfbPay exception: ", e);

            RespUtil.writeFail(response, "fail");
        }
    }

    @Autowired
    public void setPayWxProperties(WxPayProperties wxPayProperties) {
        this.wxPayProperties = wxPayProperties;
    }
}
